package service

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

type Option func(*App)

type ShutdownCallback func(ctx context.Context)

func WithShutdownCallbacks(cbs ...ShutdownCallback) Option {
	return func(app *App) {
		app.cbs = cbs
	}
}

type App struct {
	servers []*Server

	shutdownTimeout time.Duration

	waitTime time.Duration

	cbTimeout time.Duration

	cbs []ShutdownCallback
}

func NewApp(servers []*Server, opts ...Option) *App {
	return &App{
		servers:         servers,
		shutdownTimeout: 30 * time.Second,
		waitTime:        10 * time.Second,
		cbTimeout:       3 * time.Second,
	}
}

func (app *App) StartAndServe() {
	for _, s := range app.servers {
		srv := s
		go func() {
			if err := srv.Start(); err != nil {
				if err == http.ErrServerClosed {
					log.Printf("服务器%s已关闭", srv.name)
				} else {
					log.Printf("服务起%s异常退出", srv.name)
				}
			}
		}()
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill,
		syscall.SIGKILL, syscall.SIGSTOP, syscall.SIGHUP, syscall.SIGINT,
		syscall.SIGQUIT, syscall.SIGILL, syscall.SIGABRT, syscall.SIGSYS, syscall.SIGTERM)
	select {
	case <-c:
		app.shutdown()

	}
}

func (app *App) shutdown() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	select {
	case <-time.After(app.shutdownTimeout):
		os.Exit(100)
	case <-ctx.Done():
		app.close()
	}
}

func (app *App) close() {
	time.Sleep(time.Second)
	log.Println("应用关闭")
}

type Server struct {
	sev  *http.Server
	name string
	mux  *serverMux
}

type serverMux struct {
	reject bool
	*http.ServeMux
}

func (s *serverMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if s.reject {
		w.WriteHeader(http.StatusServiceUnavailable)
		_, _ = w.Write([]byte("服务已关闭"))
		return
	}
	s.ServeMux.ServeHTTP(w, r)
}

func NewServer(name string, addr string) *Server {
	mux := &serverMux{ServeMux: http.NewServeMux()}
	return &Server{
		name: name,
		mux:  mux,
		sev:  &http.Server{Addr: addr, Handler: mux},
	}
}

func (s *Server) Handle(patten string, handler http.Handler) {
	s.mux.Handle(patten, handler)
}

func (s *Server) Start() error {
	return s.sev.ListenAndServe()
}

func (s *Server) rejectReq() {
	s.mux.reject = true
}

func (s *Server) stop() error {
	log.Printf("服务器%s关闭中", s.name)
	return s.sev.Shutdown(context.Background())
}
